package org.groovymud.object.registry.domain;

import groovy.lang.Binding;
import groovy.util.ResourceException;
import groovy.util.ScriptException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.log4j.Logger;
import org.groovymud.object.MudObject;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

/**
 * used to process inline scripts that contain object definitions
 * 
 * does not use the application context, athough is aware of it so
 * it can pass it to scripts as a binding.
 *  
 *  Scripts should be of the format:
 *  {@code}
 *  import blah
 *  
 *  .. script content, class definitions etc..
 *  
 *  [objectUniqueName: new MyMudObject(x:1, y:2)]
 *  {@code}
 *  
 *  the map will be returned to the domain manager, and you can the call cloneObject on 
 *  it to get a copy of the object it's self.
 *  
 * @author corbym
 *
 */
public class ScriptedDomainManager extends AbstractSuffixedScriptedDomainManager<MudObject> implements ApplicationContextAware {

	private static final Logger logger = Logger.getLogger(BeanBuilderDomainManager.class);

	Map<String, Object> synchronizedMap = Collections.synchronizedMap(new TreeMap<String, Object>());

	public List<String> getHandles() {
		return new ArrayList<String>(synchronizedMap.keySet());
	}

	public boolean canLoad(String scriptLocation) {
		Resource r = new FileSystemResource(scriptLocation);
		if (!r.exists()) {
			r = new ClassPathResource(scriptLocation);
		}
		return matchesSuffixe(scriptLocation) && r.exists();
	}

	public MudObject getObject(String handle) {
		return (MudObject) synchronizedMap.get(handle);
	}

	public boolean contains(String handle) {
		// TODO Auto-generated method stub
		return synchronizedMap.containsKey(handle);
	}

	public void removeObject(String handle) {
		synchronizedMap.remove(handle);
	}

	/**
	 * loads one domain with a given script name
	 * @param scriptLocation
	 */
	public void loadDomain(String scriptLocation) {
		Binding binding = createScriptBinding();
		try {
			if (logger.isInfoEnabled()) {
				logger.info("loading script:" + scriptLocation);
			}
			Object obj = getEngine().run(scriptLocation, binding);
			if (obj instanceof Map) {
				Map<String, Object> m = (Map<String, Object>) obj;
				synchronizedMap.putAll(m);
			}
		} catch (ResourceException e) {
			logger.error("error loading scriptLocation: " + scriptLocation);
			logger.error(e, e);
		} catch (ScriptException e) {
			logger.error(e, e);
		}

	}
}
